function caller_MonsterSpawn (onoff, args)
  if not onoff then
    return
  end

  local ent = Game.GetTriggerEntity()
  if not ent then return end
  ent = ent:ToMonster()
  if not ent then return end

  local hpbar = AnggaraNothing.HealthBar:Create( ent )
  hpbar:Set( {
    type = tonumber(args),
    visible = true,
  } )
end


function Game.Rule:OnTakeDamage (victim, attacker, damage, weapontype, hitbox)
  return AnggaraNothing.HealthBar.OnTakeDamage( victim, attacker, damage, weapontype, hitbox )
end


local function split_args (args, pattern)
  if not args then
    return
  end
  pattern = pattern or '([^;]+)'
  local result = {}
  for v in string.gmatch(args, pattern) do
    result[#result+1] = v
  end
  return table.unpack(result)
end


local tointeger = math.tointeger
local devices_hpbar = {}
function caller_DeviceSpawn (onoff, args)
  if not onoff then
    return
  end

  local x, y, z, hp, boss_type = split_args( args )
  x, y, z, hp = tointeger(x), tointeger(y), tointeger(z), tointeger(hp)
  local owner = Game.EntityBlock.Create({x=x, y=y, z=z,})
  if not owner then
    error( 'device position is invalid' )
  end

  local hpbar = AnggaraNothing.HealthBar:Create( owner )
  if hp then
    hpbar.m_maxhealth = hp
    hpbar.m_health = hp
  end
  hpbar:Set( {
    type = tonumber(boss_type),
    visible = true,
  } )

  devices_hpbar[x] = devices_hpbar[x] or {}
  devices_hpbar[x][y] = devices_hpbar[x][y] or {}
  devices_hpbar[x][y][z] = hpbar

  Game.SetTrigger(
    string.format(
      '%d;%d;%d_OnSpawn',
      x,y,z
    )
    , true
  )

  return x, y, z, hp, boss_type
end


function caller_DeviceTakeDamage (onoff, args)
  if not onoff then
    return
  end

  local x, y, z, reset, damage = split_args( args )
  x, y, z, reset, damage = tointeger(x), tointeger(y), tointeger(z), tointeger(reset), tointeger(damage)
  local owner = Game.EntityBlock.Create({x=x, y=y, z=z,})
  if not owner then
    error( 'device position is invalid' )
  end

  devices_hpbar[x] = devices_hpbar[x] or {}
  devices_hpbar[x][y] = devices_hpbar[x][y] or {}
  local hpbar = devices_hpbar[x][y][z]
  if not hpbar then
    error( 'device has no healthbar' )
  end

  local attacker = Game.GetTriggerEntity()
  if attacker then
    if attacker:IsMonster() then
      damage = attacker.damage
    --elseif attacker:IsPlayer() then
    else
      -- Default damage.
      damage = damage or 1
    end
  end

  local health_value = hpbar.m_health - damage
  hpbar:Update( health_value )

  -- When health is zero.
  if health_value <= 0 then
    Game.SetTrigger(
      string.format(
        '%d;%d;%d_OnKilled',
        x,y,z
      )
      , true
    )
    return
  end

  if reset == nil then
    reset = false
  else
    reset = (reset > 0)
  end

  if reset then
    owner:Event({ action='reset' })
    Game.SetTrigger(
      string.format(
        '%d;%d;%d_OnSpawn',
        x,y,z
      )
      , true
    )
  end

  return x, y, z, reset, damage
end
